package eval

import (
	"showen/lex"
	"showen/obj"
	"showen/parse"
	"testing"
)

func TestDelayForce(t *testing.T) {
	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	// Test basic delay and force
	input := "(force (delay (+ 1 2)))"
	lexer := lex.NewLexer(input)
	tokens := lexer.ReadTokens()
	parser := parse.NewParser(tokens)
	expr, err := parser.ParseExpression()
	if err != nil {
		t.Fatalf("error parsing expression: %v", err)
	}

	result, err := EvalExpression(expr, env)
	if err != nil {
		t.Fatalf("error evaluating expression: %v", err)
	}

	expected := "3"
	if result.Inspect() != expected {
		t.Errorf("expected %s, got %s", expected, result.Inspect())
	}
}

func TestPromiseCaching(t *testing.T) {
	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	// Test that promise is only evaluated once
	testCode := `
	(define count 0)
	(define p (delay (begin (set! count (+ count 1)) (* 5 3))))
	(force p)
	count
	`

	lexer := lex.NewLexer(testCode)
	tokens := lexer.ReadTokens()
	parser := parse.NewParser(tokens)
	exprs, err := parser.ParseProgram()
	if err != nil {
		t.Fatalf("error parsing expression: %v", err)
	}

	expected := []string{"", "", "15", "1"}
	for i, expr := range exprs {
		result, err := EvalExpression(expr, env)
		if err != nil {
			t.Fatalf("error evaluating expression: %v", err)
		}
		if result.Inspect() != expected[i] {
			t.Errorf("expected %s, got %s", expected[i], result.Inspect())
		}
	}

	// Force again and check count is still 1
	testCode = `
	(force p)
	count
	`
	lexer = lex.NewLexer(testCode)
	tokens = lexer.ReadTokens()
	parser = parse.NewParser(tokens)
	exprs, err = parser.ParseProgram()
	if err != nil {
		t.Fatalf("error parsing expression: %v", err)
	}

	expected = []string{"15", "1"}
	for i, expr := range exprs {
		result, err := EvalExpression(expr, env)
		if err != nil {
			t.Fatalf("error evaluating expression: %v", err)
		}
		if result.Inspect() != expected[i] {
			t.Errorf("expected %s, got %s", expected[i], result.Inspect())
		}
	}
}

func TestForceNonPromise(t *testing.T) {
	env := obj.NewEnvironment()
	RegisterBuiltinProcedures(env)

	// According to R4RS, force on non-promise should return the value itself
	code := "(force 42)"

	lexer := lex.NewLexer(code)
	tokens := lexer.ReadTokens()
	parser := parse.NewParser(tokens)
	expr, err := parser.ParseExpression()
	if err != nil {
		t.Fatalf("error parsing expression: %v", err)
	}

	result, err := EvalExpression(expr, env)
	if err != nil {
		t.Fatalf("error evaluating expression: %v", err)
	}

	expected := "42"
	if result.Inspect() != expected {
		t.Errorf("expected %s, got %s", expected, result.Inspect())
	}
}
